<template>
  <div class='app-container'>
    <el-collapse class="coll-wra">
      <el-collapse-item v-for="(palette, paletteIndex) in (palettes)" :title="palette['title']" :name="palette['name']" :key="paletteIndex">
        <a v-for="(_, shapeIndex) in palette['shapes']" :shapeIndex="shapeIndex" :paletteIndex="paletteIndex" ref="dragItem" :key="shapeIndex">
        </a>
      </el-collapse-item>
    </el-collapse>
    <!-- 主容器 -->
    <div ref="graphContainer" class="parent-wrapper">
    </div>
  </div>
</template>

<script>
import mxgraph from '@utils/init.js';
import keyDown from '@utils/initKeyHandler.js';
const path = require('path')

export default {
  name: '',
  module_index: 900,
  module_name: 'XML模板工具箱',
  data() {
    return {
      editor: null,
      graph: null,
      parent: null,
      num: 0,

      palettes: [],
    };
  },
  created() { },
  mounted() {
    this.init();
    this.loadArrows();
    this.$nextTick(() => {
      this.initToolBar();
    })
  },
  methods: {
    init() {
      this.editor = new mxEditor()
      // 创建全局容器
      this.graph = this.editor.graph;
      this.editor.setGraphContainer(this.$refs.graphContainer);
      // 创建全局父元素
      this.parent = this.graph.getDefaultParent();

      //   mxEvent.disableContextMenu(this.$refs.graphContainer);
      this.rubberBand = new mxRubberband(this.graph)
      keyDown(this.graph);
    },
    loadArrows() {
      let file = path.join('./arrows.xml');     // 这里拼接了路径
      let req,root,shape;
      try {
        req = mxUtils.load(file)              // 同步同步加载指定的URL并返回mxXmlRequest。
        root = req.getDocumentElement()
        shape = root.firstChild;
      } catch (error) {
        console.log('XML文件加载失败')
        console.log(error)
      }
      this.palettes.push({ name: "arrows",shapes: [],title: "箭头" });

      while (shape != null) {
        if (shape.nodeType === mxConstants.NODETYPE_ELEMENT) {
          const shapeName = shape.getAttribute('name')
          const w = shape.getAttribute('w')
          const h = shape.getAttribute('h')

          // mxStencilRegistry:添加给定的mxStencil。
          // mxStencil:基于XML节点作为描述的通用形状。
          mxStencilRegistry.addStencil(shapeName,new mxStencil(shape))
          this.palettes.find(item => item.name == 'arrows')['shapes'].push({
            name: shape.getAttribute('name'),
            width: w / 2,
            height: h / 2
          })
        }
        shape = shape.nextSibling;
      }
    },
    // 将导入的XML显示为工具栏
    initToolBar() {
      console.log(this.palettes)
      const domArray = this.$refs.dragItem;
      console.log(domArray)
      if (!(domArray instanceof Array) || domArray.length <= 0) {
        return
      }
      domArray.forEach(dom => {
        const shapeIndex = dom.getAttribute('shapeIndex')
        const paletteIndex = dom.getAttribute('paletteIndex')
        const shapeItem = this.palettes[paletteIndex]['shapes'][shapeIndex]
        console.log(shapeItem)
        const width = shapeItem['width']
        const height = shapeItem['height']
        const dragHandler = (graph,evt,cell,x,y) => {
          console.log(1)
          this.instanceGraph(this.graph,shapeItem,x,y,width,height)
        }
        // 创建预览的虚线方框
        const createDragPreview = () => {
          const elt = document.createElement('div')

          elt.style.border = '2px dotted black'
          elt.style.width = `${width}px`
          elt.style.height = `${height}px`
          return elt
        }
        // 向a中添加小工具的单项
        dom.appendChild(this.createThumb(shapeItem,width,height))
        // MxUtils.makeDraggable：添加拖拽事件，具体解释在toolsvg.main.vue文件中
        mxUtils.makeDraggable(dom,this.graph,dragHandler,createDragPreview(),0,0,true,true)
      })
    },
    // 创建小工具的单项
    createThumb(item,width,height) {
      const tmpGraph = new mxGraph(document.createElement('div'));
      // thumbBorder：给边界预留的边框的宽度
      /**
       * 在模板工具箱中的每个工具其实都是一个单独的mxgraph，展示出来的工具也就是这个mxgraph中唯一的一个cell
       * 工具箱中的mxgraph之间也是需要有边距的，也就是这个参数，否则mxgraph会靠得很近，很难看
       */
      const thumbBorder = 2;

      tmpGraph.labelsVisible = false;               // 指定标签是否应该可见。
      // .view: 保存为单元格缓存mxCellStates的mxGraphView
      // scaleAndTranslate:设置缩放和转换，并在调用revalidate和mxGraph.sizeDidChange之前触发缩放和转换事件。
      //   tmpGraph.view.scaleAndTranslate(1,0,0);

      this.instanceGraph(tmpGraph,item,0,0,width,height);

      // 返回可见图形的边界。 
      const bounds = tmpGraph.getGraphBounds();
      /**
       * 每个工具箱的小工具都是一个单独的mxgraph和里面的cell，并且这个cell是通过XML引入的外部资源，
       * 这时就会有一个问题，也就是cell和mxgraph的大小问题，并不是每个cell都可以和他的父级mxgraph有
       * 一个合适的大小，这时就要进行缩放来让cell适应这个mxgraph
       * 但是进行缩放时的缩放至多大或者说缩放的比例是个问题，要让cell完整的被mxgraph包裹住，在不改变
       * cell宽高比的情况下，应当以长边的比例作为缩放的基准，只要长边可以显示，更短的边也一定可以显示
       * 
       * 这行代码的作用就找到一个合适的缩放比例，需要考虑到给mxgraph预留的边框thumbBorder，然后通过
       * Math.min找出更小的一个比例，然后取整(*100后再向下取整)时候将这个比例转换为百分比(/100)
       */
      const s = Math.floor(Math.min((width - 2 * thumbBorder) / bounds.width,(height - 2 * thumbBorder) / bounds.height) * 100) / 100;
      // 使用取得的缩放比例进行缩放
      // .view: 保存为单元格缓存mxCellStates的mxGraphView
      // scaleAndTranslate:设置缩放和转换，并在调用revalidate和mxGraph.sizeDidChange之前触发缩放和转换事件。
      tmpGraph.view.scaleAndTranslate(s,Math.floor((width - bounds.width * s) / 2 / s - bounds.x),Math.floor((height - bounds.height * s) / 2 / s - bounds.y))

      // getCanvas: 返回包含背景、绘制和覆盖以及装饰窗格的DOM节点
      // ownerSVGElement: JS的DOM方法，引用最近的祖先元素<svg>元素的SVGSVGElement。如果给定元素是最外层的<svg>元素，则为空。
      // cloneNode: JS的DOM方法，创建指定的节点的精确拷贝，参数为true表示他还会递归复制当前节点的所有子孙节点，否则支付至当前节点
      const node = tmpGraph.view.getCanvas().ownerSVGElement.cloneNode(true);

      node.style.position = 'relative'
      node.style.overflow = 'hidden'
      node.style.cursor = 'move'
      node.style.width = `${width}px`
      node.style.height = `${height}px`
      node.style.left = `${thumbBorder}px`
      node.style.top = `${thumbBorder}px`
      node.style.display = 'inline-block'

      return node
    },

    // 创建一个实例在指定的graph中
    instanceGraph(graph,shapeItem,x,y,width,height) {
      const parent = graph.getDefaultParent()

      graph.getModel().beginUpdate()
      try {
        let vertex = graph.insertVertex(parent,null,null,x,y,width,height,`shape=${shapeItem['name']};`)

        vertex.customer = true
        console.log(vertex)
      } finally {
        graph.getModel().endUpdate()
      }
    },
  }
};
</script>

<style scoped>
.parent-wrapper {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: calc(100% - 400px);
}
.coll-wra {
  position: absolute;
  left: 200px;
  top: 0;
  bottom: 0;
  z-index: 10;
  width: 200px;
}
</style>